package com.utils;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Logger;

/**
 * 简单的HTTP请求处理器，能够以POST和GET方式访问URL。
 * @author feng
 * @date  2018-5-28
 * 
 */
@SuppressWarnings("all")
public class HttpRequestHandler {
	
	// 序列化UID
	private static final long serialVersionUID = -4304310068298079604L;
	// 数据编码
	private static final String ENCODING = "UTF-8";

	// 连接超时值
	private int connectTimeout = -1;
	// 读取超时值
	private int readTimeout = -1;
	
	private String boundary = "--------httppost123";
	
	/**
	 * 
	 * @return 获取当前的链接超时值
	 */
	public int getConnectTimeout() {
		return connectTimeout;
	}

	/**
	 * 设置连接超时值
	 * 
	 * @param connectTimeout
	 *            新的连接超时值（毫秒），-1代表不设置
	 */
	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	/**
	 * 
	 * @return 获取当前的读取超时值
	 */
	public int getReadTimeout() {
		return readTimeout;
	}

	/**
	 * 设置读取超时值
	 * 
	 * @param readTimeout
	 *            新的读取超时值（毫秒）， -1 代表不设置
	 */
	public void setReadTimeout(int readTimeout) {
		this.readTimeout = readTimeout;
	}

	/**
	 * 构造函数
	 */
	public HttpRequestHandler() {
		// empty
	}

	/**
	 * 构造函数
	 * 
	 * @param connectTimeout
	 *            连接超时值（毫秒），-1代表不设置
	 * @param readTimeout
	 *            读取超时值（毫秒），-1代表不设置
	 */
	public HttpRequestHandler(int connectTimeout, int readTimeout) {
		this.connectTimeout = connectTimeout;
		this.readTimeout = readTimeout;
	}

	/**
	 * 关闭对象。
	 * 
	 * @param f
	 *            待关闭的对象
	 */
	public static void close(Closeable f) {
		if (f != null) {
			try {
				f.close();
			} catch (IOException ex) {
				Logger.getAnonymousLogger().info(ex.toString());
			}
		}
	}

	/**
	 * POST方式访问URL
	 * @param methodUrl
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public String postRequest(URL methodUrl, HashMap<String, String> headers, List<String> params) throws Exception {
		HttpURLConnection conn = null;
		OutputStream out = null;
		InputStream in = null;
		try {
			// 构造连接
			conn = (HttpURLConnection) methodUrl.openConnection();
			if (connectTimeout != -1) {
				conn.setConnectTimeout(connectTimeout);
			}
			if (readTimeout != -1) {
				conn.setReadTimeout(readTimeout);
			}
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Content-type", "application/json;charset=UTF-8");
			conn.setDoOutput(true);
			
			//设置请求头
			if(!headers.isEmpty()){
				for(String key : headers.keySet()){
					conn.setRequestProperty(key, headers.get(key));
				}
			}
			
			conn.connect();

			// 写入POST数据
			out = conn.getOutputStream();
			CharSequence paramString = (null == params) ? "" : merge(params, "&");
			out.write(paramString.toString().getBytes(ENCODING));

			// 读取服务器响应
			in = conn.getInputStream();
			Reader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
			StringBuilder buffer = new StringBuilder();
			char[] buf = new char[1000];
			int len = 0;
			while (len >= 0) {
				buffer.append(buf, 0, len);
				len = reader.read(buf);
			}
			return buffer.toString();
		} catch (IOException ex) {
			throw new Exception("network_error", ex);
		} finally {
			close(in);
			close(out);
			if (conn != null) {
				conn.disconnect();
			}
		}
	}

	/**
	 * 执行HTTP GET请求。
	 * 
	 * @param url
	 *            请求地址
	 * @param params
	 *            请求参数
	 * @param charset
	 *            字符集，如UTF-8, GBK, GB2312
	 * @return 响应字符串
	 * @throws IOException
	 */
	public static String getRequest(String url, Map<String, String> params, String charset) throws IOException {
		HttpURLConnection conn = null;
		String rsp = null;

		try {
			String ctype = "application/x-www-form-urlencoded;charset=" + charset;
			String query = buildQuery(params, charset);
			try {
				conn = getConnection(buildGetUrl(url, query), "GET", ctype);
			} catch (IOException e) {
				Map<String, String> map = getParamsFromUrl(url);
				throw e;
			}

			try {
				rsp = getResponseAsString(conn);
			} catch (IOException e) {
				Map<String, String> map = getParamsFromUrl(url);
				throw e;
			}

		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}

		return rsp;
	}

	public static String buildQuery(Map<String, String> params, String charset) throws IOException {
		if (params == null || params.isEmpty()) {
			return null;
		}

		StringBuilder query = new StringBuilder();
		Set<Entry<String, String>> entries = params.entrySet();
		boolean hasParam = false;

		for (Entry<String, String> entry : entries) {
			String name = entry.getKey();
			String value = entry.getValue();
			// 忽略参数名或参数值为空的参数
			if (areNotEmpty(name, value)) {
				if (hasParam) {
					query.append("&");
				} else {
					hasParam = true;
				}

				query.append(name).append("=").append(URLEncoder.encode(value, charset));
			}
		}

		return query.toString();
	}

	private static URL buildGetUrl(String strUrl, String query) throws IOException {
		URL url = new URL(strUrl);
		if (isEmpty(query)) {
			return url;
		}

		if (isEmpty(url.getQuery())) {
			if (strUrl.endsWith("?")) {
				strUrl = strUrl + query;
			} else {
				strUrl = strUrl + "?" + query;
			}
		} else {
			if (strUrl.endsWith("&")) {
				strUrl = strUrl + query;
			} else {
				strUrl = strUrl + "&" + query;
			}
		}

		return new URL(strUrl);
	}

	/**
	 * 将参数列表连接为一个字符串，以delimiter为连接符
	 * 
	 * @param params
	 *            待连接的参数列表
	 * @param delimiter
	 *            两个参数之间的连接符
	 * @return 连接后的字符串
	 */
	private static CharSequence merge(List<String> params, String delimiter) {
		if (params == null || params.isEmpty()) {
			return "";
		}
		StringBuilder buffer = new StringBuilder();
		boolean isFirst = true;
		for (String param : params) {
			if (isFirst == false) {
				buffer.append(delimiter);
			} else {
				isFirst = false;
			}
			buffer.append(param.toString());
		}
		return buffer;
	}

	/**
	 * 检查指定的字符串是否为空。
	 * <ul>
	 * <li>SysUtils.isEmpty(null) = true</li>
	 * <li>SysUtils.isEmpty("") = true</li>
	 * <li>SysUtils.isEmpty("   ") = true</li>
	 * <li>SysUtils.isEmpty("abc") = false</li>
	 * </ul>
	 * 
	 * @param value
	 *            待检查的字符串
	 * @return true/false
	 */
	public static boolean isEmpty(String value) {
		int strLen;
		if (value == null || (strLen = value.length()) == 0) {
			return true;
		}
		for (int i = 0; i < strLen; i++) {
			if ((Character.isWhitespace(value.charAt(i)) == false)) {
				return false;
			}
		}
		return true;
	}

	private static Map<String, String> getParamsFromUrl(String url) {
		Map<String, String> map = null;
		if (url != null && url.indexOf('?') != -1) {
			map = splitUrlQuery(url.substring(url.indexOf('?') + 1));
		}
		if (map == null) {
			map = new HashMap<String, String>();
		}
		return map;
	}

	/**
	 * 从URL中提取所有的参数。
	 * 
	 * @param query
	 *            URL地址
	 * @return 参数映射
	 */
	public static Map<String, String> splitUrlQuery(String query) {
		Map<String, String> result = new HashMap<String, String>();

		String[] pairs = query.split("&");
		if (pairs != null && pairs.length > 0) {
			for (String pair : pairs) {
				String[] param = pair.split("=", 2);
				if (param != null && param.length == 2) {
					result.put(param[0], param[1]);
				}
			}
		}

		return result;
	}

	/**
	 * 检查指定的字符串列表是否不为空。
	 */
	public static boolean areNotEmpty(String... values) {
		boolean result = true;
		if (values == null || values.length == 0) {
			result = false;
		} else {
			for (String value : values) {
				result &= !isEmpty(value);
			}
		}
		return result;
	}

	protected static String getResponseAsString(HttpURLConnection conn) throws IOException {
		String charset = getResponseCharset(conn.getContentType());
		InputStream es = conn.getErrorStream();
		if (es == null) {
			return getStreamAsString(conn.getInputStream(), charset);
		} else {
			String msg = getStreamAsString(es, charset);
			if (isEmpty(msg)) {
				throw new IOException(conn.getResponseCode() + ":" + conn.getResponseMessage());
			} else {
				throw new IOException(msg);
			}
		}
	}

	private static String getStreamAsString(InputStream stream, String charset) throws IOException {
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset));
			StringWriter writer = new StringWriter();

			char[] chars = new char[256];
			int count = 0;
			while ((count = reader.read(chars)) > 0) {
				writer.write(chars, 0, count);
			}

			return writer.toString();
		} finally {
			if (stream != null) {
				stream.close();
			}
		}
	}

	private static String getResponseCharset(String ctype) {
		String charset = ENCODING;

		if (!isEmpty(ctype)) {
			String[] params = ctype.split(";");
			for (String param : params) {
				param = param.trim();
				if (param.startsWith("charset")) {
					String[] pair = param.split("=", 2);
					if (pair.length == 2) {
						if (!isEmpty(pair[1])) {
							charset = pair[1].trim();
						}
					}
					break;
				}
			}
		}

		return charset;
	}

	private static HttpURLConnection getConnection(URL url, String method, String ctype) throws IOException {
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod(method);
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Accept", "text/xml,text/javascript,text/html");
		conn.setRequestProperty("User-Agent", "top-sdk-java");
		conn.setRequestProperty("Content-Type", ctype);
		return conn;
	}
	
/**
 * 以下是文件上传部分
 * @param url
 * @param headers
 * @param fileParams
 */	
	//发送数据到服务器，返回一个字节包含服务器的返回结果的数组  
    public String postUploadRequest(URL url, HashMap<String, String> headers, List<File> fileParams) throws Exception {  
    	HttpURLConnection conn = null;
		OutputStream out = null;
		InputStream in = null;
		try {
			//构造连接
			conn = initUploadConnection(url);
			if (connectTimeout != -1) {
				conn.setConnectTimeout(connectTimeout);
			}
			if (readTimeout != -1) {
				conn.setReadTimeout(readTimeout);
			}
			
			//设置请求头
			if(!headers.isEmpty()){
				for(String key : headers.keySet()){
					conn.setRequestProperty(key, headers.get(key));
				}
			}
			
			conn.connect();

			DataOutputStream ds = new DataOutputStream(conn.getOutputStream());  
			
			//文件数据
			for (Iterator<File> iterator = fileParams.iterator(); iterator.hasNext();) {
				File f = iterator.next();
				String name = f.getName();
				ds.writeBytes("--" + boundary + "\r\n");  
	            ds.writeBytes("Content-Disposition: form-data; name=\"" + name  
	                    + "\"; filename=\"" + encode(f.getName()) + "\"\r\n");  
	            ds.writeBytes("Content-Type: " + getContentType(f) + "\r\n");  
	            ds.writeBytes("\r\n");  
	            ds.write(getBytes(f));  
	            ds.writeBytes("\r\n");
			}
			//添加结尾数据  
		    ds.writeBytes("--" + boundary + "--" + "\r\n");  
		    ds.writeBytes("\r\n");

			// 读取服务器响应
			in = conn.getInputStream();
			Reader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
			StringBuilder buffer = new StringBuilder();
			char[] buf = new char[1000];
			int len = 0;
			while (len >= 0) {
				buffer.append(buf, 0, len);
				len = reader.read(buf);
			}
			return buffer.toString();
		} catch (IOException ex) {
			throw new Exception("network_error", ex);
		} finally {
			close(in);
			close(out);
			if (conn != null) {
				conn.disconnect();
			}
		}
    }
	//文件上传的connection的一些必须设置  
    private HttpURLConnection initUploadConnection(URL url) throws Exception {  
    	HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
        conn.setDoOutput(true);  
        conn.setUseCaches(false);  
        conn.setConnectTimeout(10000); //连接超时为10秒  
        conn.setRequestMethod("POST");  
        conn.setRequestProperty("Content-Type",  
                "multipart/form-data; boundary=" + boundary);
        return conn;
    }
    //获取文件的上传类型，图片格式为image/png,image/jpg等。非图片为application/octet-stream  
    private String getContentType(File f) throws Exception {  
//      return "application/octet-stream";  // 此行不再细分是否为图片，全部作为application/octet-stream 类型  
        ImageInputStream imagein = ImageIO.createImageInputStream(f);  
        if (imagein == null) {  
            return "application/octet-stream";  
        }  
        Iterator<ImageReader> it = ImageIO.getImageReaders(imagein);  
        if (!it.hasNext()) {  
            imagein.close();  
            return "application/octet-stream";  
        }  
        imagein.close();  
        return "image/" + it.next().getFormatName().toLowerCase();//将FormatName返回的值转换成小写，默认为大写  
  
    }  
    //把文件转换成字节数组  
    private byte[] getBytes(File f) throws Exception {  
        FileInputStream in = new FileInputStream(f);  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        byte[] b = new byte[1024];  
        int n;  
        while ((n = in.read(b)) != -1) {  
            out.write(b, 0, n);  
        }  
        in.close();  
        return out.toByteArray();  
    }  
    // 对包含中文的字符串进行转码，此为UTF-8。服务器那边要进行一次解码  
    private String encode(String value) throws Exception{  
        return URLEncoder.encode(value, "UTF-8");  
    }
}
